#region Copyright RenGuiYou. All rights reserved.
//=====================================================
// NeatlyFrameWork
// Author:      RenGuiyou
// Feedback: 	mailto:750539605@qq.com
//=====================================================
#endregion
using System;
using UnityEngine;
using System.Collections.Generic;
using Neatly.UI;
using UnityEngine.UI;

namespace Neatly.Tween
{
    public class NeatlyTween : MonoBehaviour
    {
        private static NeatlyTween m_Instance;
        public static NeatlyTween Instance
        {
            get
            {
                if (m_Instance == null)
                {
                    Init();
                }
                return m_Instance;
            }
        }

        public static void Init()
        {
            GameObject go = new GameObject("~NeatlyTween");
            m_Instance = go.AddComponent<NeatlyTween>();
            DontDestroyOnLoad(go);
        }

        private static uint m_counter = 0;
        private static uint Counter
        {
            get
            {
                m_counter++;
                if (m_counter > uint.MaxValue - 1)
                {
                    m_counter = 1;
                }
                return m_counter;
            }
        }
        public delegate Vector2 PositionDelegate(float y);
        public delegate float AlphaDelegate(float y);
        private readonly List<NTweenImp> m_TweenList = new List<NTweenImp>();
        public TweenObjectPool<TweenRtPosObj> TweenRtPosPool = new TweenObjectPool<TweenRtPosObj>();
        public TweenObjectPool<NTweenImp> ImplementPool = new TweenObjectPool<NTweenImp>();

        private static NTweenImp CreateImplement(TweenObjectBase tweenObject)
        {
            NTweenImp imp = Instance.ImplementPool.Create().Init(Counter, tweenObject);
            Instance.m_TweenList.Add(imp);
            return imp;
        }

        // spline move
        public static NTweenImp FuncMove(RectTransform rt, float targetY, float time, PositionDelegate posResult,
            AlphaDelegate alphaResult)
        {
            TweenObjectBase tween = TweenFuncPosObj.Create(rt, targetY, time, posResult, alphaResult);
            return CreateImplement(tween);
        }

        public static NTweenImp Move(GameObject gameObject, float x, float y, float z, float time)
        {
            return Move(gameObject, new Vector3(x, y, z), time);
        }

        // Move (World Position)
        public static NTweenImp Move(GameObject gameObject, Vector3 target, float time, int tweenType = 0)
        {
            TweenObjectBase tween = TweenRtPosObj.CreateMove(gameObject, target, time, tweenType);
            return CreateImplement(tween);
        }

        // Move Bezier
        public static NTweenImp MoveBezierMove(GameObject gameObject, Vector3 target, List<Vector3> bezierList, float time, int tweenType = 0)
        {
            TweenObjectBase tween = TweenRtPosObj.CreateBezierMove(gameObject, target, bezierList, time, tweenType);
            return CreateImplement(tween);
        }

        public static NTweenImp MoveLocal(GameObject gameObject, float x, float y, float z, float time)
        {
            return MoveLocal(gameObject, new Vector3(x, y, z), time);
        }

        public static NTweenImp MoveLocal(GameObject gameObject, float x, float y, float z, float time, int tweenType = 0)
        {
            return MoveLocal(gameObject, new Vector3(x, y, z), time, tweenType);
        }

        // Move (LocalPosition)
        public static NTweenImp MoveLocal(GameObject gameObject, Vector3 target, float time, int tweenType = 0)
        {
            TweenObjectBase tween = TweenRtPosObj.CreateMoveLocal(gameObject, target, time, tweenType);
            return CreateImplement(tween);
        }

        public static NTweenImp MoveUI(RectTransform rt, float x, float y, float time, int tweenType = 0)
        {
            return MoveUI(rt, new Vector2(x, y), time, tweenType);
        }

        public static NTweenImp MoveUI(Component cmp, float x, float y, float time, int tweenType = 0)
        {
            return MoveUI(cmp, new Vector2(x, y), time, tweenType);
        }

        // Move (UI)
        public static NTweenImp MoveUI(RectTransform rt, Vector2 target, float time, int tweenType = 0)
        {
            TweenObjectBase tween = TweenRtPosObj.CreateMove(rt, target, time, tweenType);
            return CreateImplement(tween);
        }

        public static NTweenImp MoveUI(Component behaviour, Vector2 target, float time, int tweenType = 0)
        {
            TweenObjectBase tween = TweenRtPosObj.CreateMove(behaviour, target, time, tweenType);
            return CreateImplement(tween);
        }

        // Move Bezier (UI)
        public static NTweenImp MoveUIBezier(RectTransform rt, Vector2 target, Vector2 bezier, float time)
        {
            TweenObjectBase tween = TweenRtPosObj.CreateUIBezierMove(rt, target, bezier, time);
            return CreateImplement(tween);
        }

        // Move Bezier (UI)
        public static NTweenImp MoveUIBezier(RectTransform rt, RectTransform target, float percent, float y, float time)
        {
            var bezier = NMathf.Lerp(rt.anchoredPosition, target.anchoredPosition, percent);
            bezier.y = y;
            TweenObjectBase tween = TweenRtPosObj.CreateUIBezierMove(rt, target.anchoredPosition, bezier, time);
            return CreateImplement(tween);
        }

        public static NTweenImp MoveUIBezierList(RectTransform rt, float x, float y, float time, float p1, float by1, float p2, float by2)
        {
            List<Vector3> bezierList = new List<Vector3>();
            float startX = rt.GetAnchoredPositionX();
            bezierList.Add(rt.GetAnchoredPosition());
            bezierList.Add(new Vector3(Mathf.Lerp(startX, x, p1), by1));
            bezierList.Add(new Vector3(Mathf.Lerp(startX, x, p2), by2));
            bezierList.Add(new Vector2(x, y));
            TweenObjectBase tween = TweenRtPosObj.CreateUIBezierMove(rt, new Vector2(x, y), bezierList, time);
            return CreateImplement(tween);
        }

        public static NTweenImp MoveUIBezierList(RectTransform rt, float x, float y, float time, float p1, float by1)
        {
            List<Vector3> bezierList = new List<Vector3>();
            float startX = rt.GetAnchoredPositionX();
            bezierList.Add(rt.GetAnchoredPosition());
            bezierList.Add(new Vector3(Mathf.Lerp(startX, x, p1), by1));
            bezierList.Add(new Vector2(x, y));
            TweenObjectBase tween = TweenRtPosObj.CreateUIBezierMove(rt, new Vector2(x, y), bezierList, time);
            return CreateImplement(tween);
        }
        
        // Move Bezier (UI)
        public static NTweenImp MoveBezierHeight(RectTransform rt, float targetX, float targetY, float percent, float directAngle, float height, float time, int tweenType = 0)
        {
            Vector2 target = new Vector2(targetX, targetY);
            Vector2 start = rt.anchoredPosition;
            Vector2 bezier = NMathf.Lerp(start, target, percent);
            Vector2 direct = start - target;
            float rad = Mathf.Deg2Rad * directAngle;
            float x = direct.x * Mathf.Cos(rad) + direct.y * Mathf.Sin(rad);
            float y = direct.x * -Mathf.Sin(rad) + direct.y * Mathf.Cos(rad);
            direct = new Vector2(x, y);
            bezier += direct.normalized * height;
            TweenObjectBase tween = TweenRtPosObj.CreateUIBezierMove(rt, target, bezier, time, tweenType);
            return CreateImplement(tween);
        }

        // Move Bezier (UI)
        public static NTweenImp MoveUIBezier(RectTransform rt, Vector2 target, float percent, float bezierY, float time)
        {
            var bezier = NMathf.Lerp(rt.anchoredPosition, target, percent);
            bezier.y = bezierY;
            TweenObjectBase tween = TweenRtPosObj.CreateUIBezierMove(rt, target, bezier, time);
            return CreateImplement(tween);
        }

        // Move Bezier (UI)
        public static NTweenImp MoveUIBezier(RectTransform rt, float x, float y, float percent, float bezierY, float time)
        {
            return MoveUIBezier(rt, new Vector2(x, y), percent, bezierY, time);
        }

        // Move X(UI)
        public static NTweenImp MoveUIX(RectTransform rt, float x, float time, int tweenType = 0)
        {
            Vector2 target = new Vector2(x, rt.anchoredPosition.y);
            return MoveUI(rt, target, time, tweenType);
        }

        public static NTweenImp MoveUIX(Component cmp, float x, float time, int tweenType = 0)
        {
            Vector2 target = new Vector2(x, cmp.transform.rectTransform().anchoredPosition.y);
            return MoveUI(cmp, target, time, tweenType);
        }

        // Move Y(UI)
        public static NTweenImp MoveUIY(RectTransform rt, float y, float time, int tweenType = 0)
        {
            Vector2 target = new Vector2(rt.anchoredPosition.x, y);
            return MoveUI(rt, target, time, tweenType);
        }

        public static NTweenImp MoveUIY(Component cmp, float y, float time, int tweenType = 0)
        {
            Vector2 target = new Vector2(cmp.transform.rectTransform().anchoredPosition.x, y);
            return MoveUI(cmp, target, time, tweenType);
        }

        public static NTweenImp SizeDeltaX(RectTransform rt, float val, float time)
        {
            Vector2 target = new Vector2(val, rt.sizeDelta.y);
            return SizeDelta(rt, target, time);
        }

        public static NTweenImp SizeDeltaX(Component cmp, float val, float time)
        {
            Vector2 target = new Vector2(val, cmp.rectTransform().sizeDelta.y);
            return SizeDelta(cmp, target, time);
        }

        public static NTweenImp SizeDeltaY(Component cmp, float val, float time)
        {
            Vector2 target = new Vector2(cmp.rectTransform().sizeDelta.x, val);
            return SizeDelta(cmp, target, time);
        }

        public static NTweenImp SizeDeltaY(RectTransform rt, float val, float time)
        {
            Vector2 target = new Vector2(rt.sizeDelta.x, val);
            return SizeDelta(rt, target, time);
        }

        public static NTweenImp SizeDelta(RectTransform rt, Vector2 target, float time)
        {
            TweenObjectBase tween = TweenRtPosObj.CreateSizeDelta(rt, target, time);
            return CreateImplement(tween);
        }

        public static NTweenImp SizeDelta(Component cmp, Vector2 target, float time)
        {
            TweenObjectBase tween = TweenRtPosObj.CreateSizeDelta(cmp, target, time);
            return CreateImplement(tween);
        }

        // Scale
        public static NTweenImp Scale(Transform rt, Vector3 target, float time)
        {
            TweenObjectBase tween = TweenRtPosObj.CreateScale(rt, target, time);
            return CreateImplement(tween);
        }

        public static NTweenImp Scale(Transform rt, float scale, float time)
        {
            TweenObjectBase tween = TweenRtPosObj.CreateScale(rt, new Vector3(scale, scale, scale), time);
            return CreateImplement(tween);
        }

        public static NTweenImp ScaleEaseOutQuint(Transform rt, float scale, float time)
        {
            TweenObjectBase tween = TweenRtPosObj.CreateScaleEaseOutQuint(rt, new Vector3(scale, scale, scale), time);
            return CreateImplement(tween);
        }

        // ScaleY
        public static NTweenImp ScaleY(Transform rt, float target, float time)
        {
            Vector3 v = rt.localScale;
            v.y = target;
            TweenObjectBase tween = TweenRtPosObj.CreateScale(rt, v, time);
            return CreateImplement(tween);
        }

        // Rotate
        public static NTweenImp Rotate(Transform rt, Vector3 target, float time)
        {
            TweenObjectBase tween = TweenRtPosObj.CreateRotate(rt, target, time);
            return CreateImplement(tween);
        }

        public static NTweenImp RotateX(Transform rt, float angle, float time)
        {
            Vector3 euler = rt.localEulerAngles;
            euler.x = angle;
            return Rotate(rt, euler, time);
        }

        public static NTweenImp RotateY(Transform rt, float angle, float time)
        {
            Vector3 euler = rt.localEulerAngles;
            euler.y = angle;
            return Rotate(rt, euler, time);
        }

        public static NTweenImp RotateZ(Transform rt, float angle, float time)
        {
            Vector3 euler = rt.localEulerAngles;
            euler.z = angle;
            return Rotate(rt, euler, time);
        }

        public static NTweenImp AutoRotate(Transform rt, Vector3 target, float time)
        {
            TweenObjectBase tween = TweenRtPosObj.CreateAutoRotate(rt, target, time);
            return CreateImplement(tween);
        }

        public static NTweenImp AutoRotateX(Transform rt, float angle, float time)
        {
            Vector3 euler = rt.localEulerAngles;
            euler.x = angle;
            return AutoRotate(rt, euler, time);
        }

        public static NTweenImp AutoRotateY(Transform rt, float angle, float time)
        {
            Vector3 euler = rt.localEulerAngles;
            euler.y = angle;
            return AutoRotate(rt, euler, time);
        }

        public static NTweenImp AutoRotateZ(Transform rt, float angle, float time)
        {
            Vector3 euler = rt.localEulerAngles;
            euler.z = angle;
            return AutoRotate(rt, euler, time);
        }

        // Graphic Color
        public static NTweenImp Color(Graphic graphic, Color target, float time)
        {
            TweenObjectBase tween = TweenRtPosObj.CreateColor(graphic, target, time);
            return CreateImplement(tween);
        }

        // Graphic Color
        public static NTweenImp ColorAlpha(Graphic graphic, float target, float time)
        {
            Color c = graphic.color;
            c.a = target;
            TweenObjectBase tween = TweenRtPosObj.CreateColor(graphic, c, time);
            return CreateImplement(tween);
        }

        // Graphic Color
        public static NTweenImp ColorAlpha(SpriteRenderer graphic, float target, float time)
        {
            Color c = graphic.color;
            c.a = target;
            TweenObjectBase tween = TweenRtPosObj.CreateColor(graphic, c, time);
            return CreateImplement(tween);
        }

        public static NTweenImp MultiFillAmount(NImage image, int lastMultiCount, float targetVal, int multiCount, Action<int> changeAction, float time)
        {
            TweenObjectBase tween = TweenMultiProgress.CreateFillAmount(image, lastMultiCount, targetVal, multiCount, changeAction, time);
            return CreateImplement(tween);
        }

        public static NTweenImp MultiSizeDelta(NImage image, int maxWidth, int lastMultiCount, float targetVal, int multiCount, Action<int> changeAction, float time)
        {
            TweenObjectBase tween = TweenMultiProgress.CreateSizeDelta(image, maxWidth, lastMultiCount, targetVal, multiCount, changeAction, time);
            return CreateImplement(tween);
        }

        // 文本跳动
        public static NTweenImp JumpText(TextInterface text, double startVal, double targetVal, float time, string beforeLabel = null, string afterLabel = null)
        {
            TweenObjectBase tween = TweenJumpObj.Create(text, startVal, targetVal, time, beforeLabel, afterLabel);
            return CreateImplement(tween);
        }

        // 文本跳动
        public static NTweenImp JumpFormatText(TextInterface text, double startVal, double targetVal, float time, string beforeLabel = null, string afterLabel = null)
        {
            TweenObjectBase tween = TweenJumpObj.CreateFormat(text, startVal, targetVal, time, beforeLabel, afterLabel);
            return CreateImplement(tween);
        }

        // slider移动(直接到目标值)
        public static NTweenImp SliderMove(NSlider slider, float targetVal, float time)
        {
            TweenObjectBase tween = TweenSliderObj.Create(slider, targetVal, time);
            return CreateImplement(tween);
        }

        // slider移动(先到终点)
        public static NTweenImp SliderMoveDirect(Slider slider, float targetVal, float time, bool direct)
        {
            TweenObjectBase tween = TweenSliderDirectObj.Create(slider, targetVal, time, direct);
            return CreateImplement(tween);
        }

        // ImageFill
        public static NTweenImp ImageFill(NImage image, float targetVal, float time)
        {
            TweenObjectBase tween = TweenImgFillObj.Create(image, targetVal, time);
            return CreateImplement(tween);
        }

        void Update()
        {
            for (int i = m_TweenList.Count - 1; i >= 0; i--)
            {
                if (m_TweenList[i].IsDestroy)
                {
                    m_TweenList[i].Destroy();
                    m_TweenList.RemoveAt(i);
                    continue;
                }
                m_TweenList[i].Execute(Time.deltaTime);
            }
        }

        public static void Cancel(uint id)
        {
            if (id <= 0) return;
            for (int i = 0; i < Instance.m_TweenList.Count; i++)
            {
                if (Instance.m_TweenList[i].id == id)
                {
                    Instance.m_TweenList[i].Cancel();
                }
            }
        }

        public static void Cancel(GameObject gameObject)
        {
            if (Instance == null || gameObject == null) return;
            for (int i = 0; i < Instance.m_TweenList.Count; i++)
            {
                if (Instance.m_TweenList[i].gameObject == gameObject)
                {
                    Instance.m_TweenList[i].Cancel();
                }
            }
        }

        public static void Cancel(Component cmp)
        {
            if (Instance == null || cmp == null) return;
            for (int i = 0; i < Instance.m_TweenList.Count; i++)
            {
                if (Instance.m_TweenList[i].gameObject == cmp.gameObject)
                {
                    Instance.m_TweenList[i].Cancel();
                }
            }
        }

        public static void Pause(uint id)
        {
            for (int i = 0; i < Instance.m_TweenList.Count; i++)
            {
                if (Instance.m_TweenList[i].id == id)
                {
                    Instance.m_TweenList[i].Pause();
                }
            }
        }

        public static void Resume(uint id)
        {
            for (int i = 0; i < Instance.m_TweenList.Count; i++)
            {
                if (Instance.m_TweenList[i].id == id)
                {
                    Instance.m_TweenList[i].Resume();
                }
            }
        }
    }
}